package io.kimmking.chatoom.server.boot;

import io.kimmking.chatoom.server.config.NettyServerConfig;
import io.kimmking.chatoom.server.handler.NettyServerMsgHandler;
import io.kimmking.common.zookeeper.annotation.registerZK;
import io.kimmking.common.zookeeper.zktemplate.ZookeeperTemplate;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.*;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;

import java.util.Objects;

@Slf4j
//@Component
//@ConditionalOnBean(NettyServerConfig.class)
public class NettyServerBootStrap {

    private final static String rootPath = "netty_servers";

//    @Resource
    private NettyServerConfig nettyServerConfig;

    private final NettyServerMsgHandler nettyServerMsgHandler;
    private final ZookeeperTemplate zookeeperTemplate;

    private final EventLoopGroup boss = new NioEventLoopGroup(Objects.requireNonNull(nettyServerConfig.getBossLoopGroups(), "BoosLoopGroup number can not be null"));
    private final EventLoopGroup worker = new NioEventLoopGroup(nettyServerConfig.getWorkerLoopGroups());
    private final ServerBootstrap boot = new ServerBootstrap();

    private Channel channel;

    public NettyServerBootStrap (NettyServerConfig nettyServerConfig,
                                 final NettyServerMsgHandler nettyServerMsgHandler,
                                 final ZookeeperTemplate zookeeperTemplate) {
        this.nettyServerConfig = nettyServerConfig;
        this.nettyServerMsgHandler = nettyServerMsgHandler;
        this.zookeeperTemplate = zookeeperTemplate;
    }

    public void start() throws Exception {

        boot.group(boss, worker);
        boot.channel(NioServerSocketChannel.class);
        boot.option(ChannelOption.SO_BACKLOG, nettyServerConfig.getSoBacklog());
        boot.childOption(ChannelOption.SO_KEEPALIVE, nettyServerConfig.isSoKeepAlive());
        boot.childHandler(new ChannelInitializer<SocketChannel>() {
            @Override
            protected void initChannel(SocketChannel socketChannel) throws Exception {
                ChannelPipeline pipeline = socketChannel.pipeline();
                pipeline.addLast("encoder", new StringEncoder());
                pipeline.addLast("decoder", new StringEncoder());
                pipeline.addLast(nettyServerMsgHandler);
            }
        });
        log.info("Netty服务端开始启动!");

        //绑定端口操作
        ChannelFuture channelFuture = boot.bind(NettyServerConfig.getHostName() ,nettyServerConfig.getPort()).sync();
        channelFuture.addListener(future -> {
            if(future.isSuccess())
                log.info("Netty服务器正在启动中。。。。。。。");
            if(future.isDone()) {
                log.info("Netty服务器启动成功。。。。。。。");
                registerNettyServer();
                log.info("NettyServer Info has been register to ZK ");
            }
        });

        channel = channelFuture.channel();
        //监听channel关闭事件
        channel.closeFuture().sync();
        channelFuture.addListener(future -> {
            if(future.isCancelled())
                log.info("Netty服务器正在关闭中。。。。。。。");
            if(future.isCancellable())
                log.info("Netty服务器已经关闭。。。。。。。");
        });
    }

    /**
     * todo 聊天室Server下线时，需要将Redis里UserName和聊天室NettyServer的路由信息删除
     * @throws Exception
     */
    public void close() throws Exception {
        Objects.requireNonNull(channel, "channel is null!").close();

        NettyServerConfig.channelGroup.close();

        boss.shutdownGracefully();
        worker.shutdownGracefully();
    }

    /**
     * 将nettyServer的地址信息注册到ZK上，注册为临时节点
     */
    @registerZK(port = "${km.netty.server.port}", rootPath = rootPath)
    private void registerNettyServer(){
    }
}
